package worker

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"time"

	"zhonghui/console/warehouse/cfgx"
	"zhonghui/console/warehouse/dbx"
	"zhonghui/console/warehouse/dbx/cache_main"
	"zhonghui/console/warehouse/helperx"
	"zhonghui/console/warehouse/logx"
	"zhonghui/console/wxutil"

	jsoniter "github.com/json-iterator/go"
)

// MpAccessTokenWorker 刷新小程序调用凭据AccessToken
type MpAccessTokenWorker struct {
}

func (w *MpAccessTokenWorker) Start() {
	w.do()
	t := time.NewTicker(60 * time.Second)
	for {
		select {
		case <-t.C:
			w.do()
		}
	}
}

func (w *MpAccessTokenWorker) do() {
	defer func() {
		if r := recover(); r != nil {
			logx.Zap().Errorw("定时任务异常", "err", errors.New(fmt.Sprintf("%s", r)))
		}
	}()

	rc := cache_main.Client()
	lock, ch, err := dbx.GetRedisLockWithRefresh(rc, dbx.RedisPrefix("locker-MpAccessTokenWorker"), 5*time.Second, time.Second)
	if err != nil {
		return
	}
	defer func() {
		if ch != nil {
			ch <- 1
		}
		lock.Release(context.Background())
	}()

	logx.Zap().Infow("开始检查mp_access_token，获取锁成功")
	cfg := cfgx.Cfg().GetStringMapString("mp")

	// 获取token的有效时间
	t, err := wxutil.GetMpAccessTokenTtl(cfg["app_id"])
	if err != nil {
		logx.Zap().Errorw("mp_access_token从缓存获取失败", "err", err)
		return
	}

	// 如果大于10分钟则有效
	if t.Seconds() > 600 {
		logx.Zap().Infow("mp_access_token当前有效期", "ttl", t.Seconds())
		return
	}

	logx.Zap().Infow("mp_access_token未找到开始获取")

	// 检查锁
	if true != dbx.HasRedisLock(lock) {
		logx.Zap().Warnw("请求前没有锁放弃写入")
		return
	}

	res, err := helperx.SendHttpRequest(context.Background(), http.MethodGet,
		fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", cfg["app_id"], cfg["secret"]),
		nil, nil, false, 0)
	if err != nil {
		logx.Zap().Errorw("获取mp_access_token失败", "err", err)
		return
	}

	// 解析返回数据
	data := struct {
		ErrCode     int64  `json:"errcode"`
		ErrMsg      string `json:"errmsg"`
		AccessToken string `json:"access_token"`
		ExpiresIn   int64  `json:"expires_in"`
	}{}
	if err := jsoniter.Unmarshal(res, &data); err != nil {
		logx.Zap().Errorw("解析mp_access_token失败", "err", err)
		return
	}

	// 业务失败
	if data.ErrCode != 0 {
		logx.Zap().Errorw("获取mp_access_token失败", "err", errors.New(data.ErrMsg))
		return
	}

	// 开始保存
	if err := wxutil.SetMpAccessToken(cfg["app_id"], data.AccessToken); err != nil {
		logx.Zap().Errorw("写入mp_access_token失败", "err", err)
		return
	}
}
